﻿
#ifdef USE_SDL


#include "VirtualEvent.h"
#include "SdlApplication.h"

#include "Resource.h"
#ifdef AUTO_LOAD_FONT_DATA
//#include "LuaGuiFontDataResource.h"
#include "AutoGenerateLuaGuiFontDataResource.h"
#endif // AUTO_LOAD_FONT_DATA

#ifdef AUTO_LOAD_SCRIPT_DATA
#include "AutoGenerateLuaGuiScriptResource.h"
//#include "LuaGuiScriptResource.h"
#endif // AUTO_LOAD_SCRIPT_DATA

namespace LuaGui {
	SdlApplication::SdlApplication() {
		m_cr = nullptr;
		m_sdl_window = nullptr;
		m_sdl_surface = nullptr;
		m_font_data = nullptr;
		m_paint = nullptr;

		m_window_width = 1280;
		m_window_height = 720;

        //启动SDL
		SDL_Init(SDL_INIT_EVERYTHING);
		SDL_SetHint(SDL_HINT_IME_SHOW_UI, "1");
	}

	SdlApplication::~SdlApplication() {
		if (m_cr) {
			cairo_surface_t* surface = cairo_get_target(m_cr);
			cairo_surface_destroy(surface);
			cairo_destroy(m_cr);
			m_cr = nullptr;
		}
		if (m_sdl_surface) {
			SDL_FreeSurface(m_sdl_surface);
			m_sdl_surface = nullptr;
		}
		if (m_paint) {
			delete m_paint;
			m_paint = nullptr;
		}
		//退出SDL 
		SDL_Quit();
	}
	std::string SdlApplication::GetReadWriteDirectory() {
#if defined(ANDROID) || defined(__android) || defined(__android__)
		return SDL_AndroidGetExternalStoragePath();
#else
		return Application::GetReadWriteDirectory();
#endif // ANDROID
	}

	void SdlApplication::OnInit() {
		Application::OnInit();

		SDL_DisplayMode dm;
		if (SDL_GetDesktopDisplayMode(0, &dm) != 0) {
			SDL_Log("SDL_GetDesktopDisplayMode failed: %s", SDL_GetError());
			return;
		}

		this->SetScreenWidthHeight(dm.w, dm.h);
		SDL_Log("screen width = %d, height = %d", dm.w, dm.h);

		m_paint = new LuaGui::CairoPaint(m_cr);

		m_sdl_window = SDL_CreateWindow(m_window_title.c_str(), SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, m_window_width, m_window_height, SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_RESIZABLE);

#if defined(AUTO_LOAD_FONT_DATA)
		LuaGuiResource* fontdata = LOAD_RESOURCE("font_ttf");
		m_paint->SetFont((unsigned char*)(fontdata.data()), (uint32_t)fontdata.size());
#elif defined(AUTO_LOAD_FONT_FILE)
		if (!m_font_filepath.empty()) {
			m_paint->SetFont(m_font_filepath.u8string());
		}
#endif
	}

	void SdlApplication::OnLoopOnce() {
		Application::OnLoopOnce();
		CairoImage::CollectGarbage();  // 释放长时间不使用图片资源
	}

	void SdlApplication::Loop() {
		while (!m_exited) {
			SDL_Event sdl_event;
			while (SDL_PollEvent(&sdl_event)) {
				if (sdl_event.type == SDL_QUIT) {
					this->Exit();
				}
				else if (sdl_event.type == SDL_WINDOWEVENT) {
					switch (sdl_event.window.event) {
					case SDL_WINDOWEVENT_SHOWN:
						break;
					case SDL_WINDOWEVENT_HIDDEN:
						break;
					case SDL_WINDOWEVENT_EXPOSED:
						break;
					case SDL_WINDOWEVENT_MOVED:
						SDL_Log("Window %d moved to %d,%d", sdl_event.window.windowID, sdl_event.window.data1, sdl_event.window.data2);
						break;
					case SDL_WINDOWEVENT_RESIZED:
						SDL_Log("Window %d resized to %dx%d", sdl_event.window.windowID, sdl_event.window.data1, sdl_event.window.data2);
						break;
					case SDL_WINDOWEVENT_SIZE_CHANGED:
						SDL_Log("Window %d size changed to %dx%d", sdl_event.window.windowID, sdl_event.window.data1, sdl_event.window.data2);
						this->SetWindowWidthHeight(sdl_event.window.data1, sdl_event.window.data2);
						break;
					case SDL_WINDOWEVENT_MINIMIZED:
						SDL_Log("Window %d minimized", sdl_event.window.windowID);
						break;
					case SDL_WINDOWEVENT_MAXIMIZED:
						SDL_Log("Window %d maximized", sdl_event.window.windowID);
						break;
					case SDL_WINDOWEVENT_RESTORED:
						SDL_Log("Window %d restored", sdl_event.window.windowID);
						break;
					case SDL_WINDOWEVENT_ENTER:
						SDL_Log("Mouse entered window %d", sdl_event.window.windowID);
						break;
					case SDL_WINDOWEVENT_LEAVE:
						SDL_Log("Mouse left window %d", sdl_event.window.windowID);
						break;
					case SDL_WINDOWEVENT_FOCUS_GAINED:
						SDL_Log("Window %d gained keyboard focus",
							sdl_event.window.windowID);
						break;
					case SDL_WINDOWEVENT_FOCUS_LOST:
						SDL_Log("Window %d lost keyboard focus",
							sdl_event.window.windowID);
						break;
					case SDL_WINDOWEVENT_CLOSE:
						SDL_Log("Window %d closed", sdl_event.window.windowID);
						break;
#if SDL_VERSION_ATLEAST(2, 0, 5)
					case SDL_WINDOWEVENT_TAKE_FOCUS:
						SDL_Log("Window %d is offered a focus", sdl_event.window.windowID);
						break;
					case SDL_WINDOWEVENT_HIT_TEST:
						SDL_Log("Window %d has a special hit test", sdl_event.window.windowID);
						break;
#endif
					default:
						SDL_Log("Window %d got unknown event %d", sdl_event.window.windowID, sdl_event.window.event);
						break;
					}
				}
				else if (sdl_event.type == SDL_MOUSEMOTION) {
					//SDL_Log("mousemove x = %d y = %d state = %d", sdl_event.motion.x, sdl_event.motion.y, sdl_event.motion.state);
					unsigned int mouse_key = EVirtualMouseKey::KEY_NONE;
					if (sdl_event.motion.state == SDL_BUTTON_LMASK) mouse_key = mouse_key | EVirtualMouseKey::KEY_LEFT;
					else if (sdl_event.motion.state & SDL_BUTTON_RMASK) mouse_key = mouse_key | EVirtualMouseKey::KEY_RIGHT;
					else if (sdl_event.motion.state & SDL_BUTTON_MMASK) mouse_key = mouse_key | EVirtualMouseKey::KEY_MIDDLE;
					OnMouse(std::make_shared<VirtualMouseEvent>(EVirtualEvent::MOUSE_MOVE, mouse_key, sdl_event.motion.x, sdl_event.motion.y));
				}
				else if (sdl_event.type == SDL_MOUSEBUTTONDOWN) {
					SDL_Log("mousedown x = %d y = %d button = %d state = %d(SDL_PRESSED=1,SDL_RELEASED=0)", sdl_event.button.x, sdl_event.button.y, sdl_event.button.button, sdl_event.button.state); // SDL_PRESSED
					unsigned int mouse_key = EVirtualMouseKey::KEY_NONE;
					if (sdl_event.button.button == SDL_BUTTON_LEFT) mouse_key = EVirtualMouseKey::KEY_LEFT;
					else if (sdl_event.button.button & SDL_BUTTON_RMASK) mouse_key = EVirtualMouseKey::KEY_RIGHT;
					else if (sdl_event.button.button & SDL_BUTTON_MMASK) mouse_key = EVirtualMouseKey::KEY_MIDDLE;
					OnMouse(std::make_shared<VirtualMouseEvent>(EVirtualEvent::MOUSE_DOWN, mouse_key, sdl_event.button.x, sdl_event.button.y));
				}
				else if (sdl_event.type == SDL_MOUSEBUTTONUP) {
					SDL_Log("mouseup x = %d y = %d button = %d state = %d(SDL_PRESSED=1,SDL_RELEASED=0)", sdl_event.button.x, sdl_event.button.y, sdl_event.button.button, sdl_event.button.state); // SDL_PRESSED
					unsigned int mouse_key = EVirtualMouseKey::KEY_NONE;
					if (sdl_event.button.button == SDL_BUTTON_LEFT) mouse_key = EVirtualMouseKey::KEY_LEFT;
					else if (sdl_event.button.button & SDL_BUTTON_RMASK) mouse_key = EVirtualMouseKey::KEY_RIGHT;
					else if (sdl_event.button.button & SDL_BUTTON_MMASK) mouse_key = EVirtualMouseKey::KEY_MIDDLE;
					OnMouse(std::make_shared<VirtualMouseEvent>(EVirtualEvent::MOUSE_UP, mouse_key, sdl_event.button.x, sdl_event.button.y));
				}
				else if (sdl_event.type == SDL_MOUSEWHEEL) {
					SDL_Log("mousewheel x = %d y = %d ", sdl_event.wheel.x, sdl_event.wheel.y);
					OnMouse(std::make_shared<VirtualMouseEvent>(EVirtualEvent::MOUSE_WHEEL, EVirtualMouseKey::KEY_NONE, sdl_event.wheel.x, sdl_event.wheel.y));
				}
				else if (sdl_event.type == SDL_KEYDOWN) {
					// https://wiki.libsdl.org/SDL_Keycode
					SDL_Log("keydown keycode = %d ", sdl_event.key.keysym.sym);
					unsigned int keycode = sdl_event.key.keysym.sym;
					OnKey(std::make_shared<VirtualKeyEvent>(EVirtualEvent::KEY_DOWN, keycode, SdlKeyBoard::GetSingleton()->GetVirtualKey(keycode)));
				}
				else if (sdl_event.type == SDL_KEYUP) {
					// https://wiki.libsdl.org/SDL_Keycode SDL_SCANCODE_6 SDLK_6
					SDL_Log("keyup keycode = %d ", sdl_event.key.keysym.sym);
					unsigned int keycode = sdl_event.key.keysym.sym;
					OnKey(std::make_shared<VirtualKeyEvent>(EVirtualEvent::KEY_UP, keycode, SdlKeyBoard::GetSingleton()->GetVirtualKey(keycode)));
				}
				else if (sdl_event.type == SDL_TEXTINPUT) {
					SDL_Log("textinput text = %s ", sdl_event.text.text);
					OnChar(std::make_shared<VirtualCharEvent>(EVirtualEvent::CHAR, sdl_event.text.text));
				}
				else {
					//SDL_LogInfo(0, "event_type: %x", sdl_event.type);
				}
			}

			this->OnLoopOnce();

			SDL_Delay(m_timer_interval);
		}
	}

	void SdlApplication::Render() {
		static SDL_Rect s_rect = { 0, 0, 0, 0 };

		//m_paint->Clear();
		cairo_rectangle(m_cr, 0, 0, m_screen_width, m_screen_height);
		cairo_set_source_rgba(m_cr, 1, 1, 1, 1);
		cairo_fill(m_cr);

		// TODO logic render 
	/*	m_paint->SetBrushColor(0x0000ffff);
		m_paint->DrawRectangle(20, 20, 200, 200);
		m_paint->SetFontColor(0x000000ff);
		m_paint->SetFontSize(20);
		m_paint->DrawString(400, 100, "hello world");
		m_paint->DrawString(400, 200, L"中文");*/

		GetScriptApplicationInstance()->OnPaint();

		m_paint->Flush();
		s_rect.x = 0;
		s_rect.y = 0;
		s_rect.w = m_window_width;
		s_rect.h = m_window_height;
		SDL_Surface* window_surface = SDL_GetWindowSurface(m_sdl_window);
		SDL_BlitSurface(m_sdl_surface, &s_rect, window_surface, &s_rect);
		SDL_UpdateWindowSurface(m_sdl_window);
	}

	// 定时回调
	void SdlApplication::OnTimer() {
		GetScriptApplicationInstance()->OnTimer();
	}
	// 鼠标按下事件
	void SdlApplication::OnMouseDown(int mouse_x, int mouse_y, int mouse_button) {
		GetScriptApplicationInstance()->OnMouseDown(mouse_x, mouse_y, mouse_button);
	}
	// 鼠标移动事件
	void SdlApplication::OnMouseMove(int mouse_x, int mouse_y) {
		GetScriptApplicationInstance()->OnMouseMove(mouse_x, mouse_y);
	}
	// 鼠标松开事件
	void SdlApplication::OnMouseUp(int mouse_x, int mouse_y, int mouse_button) {
		GetScriptApplicationInstance()->OnMouseUp(mouse_x, mouse_y, mouse_button);
	}
	// 鼠标滚动事件
	void SdlApplication::OnMouseWheel(int mouse_x, int mouse_y) {
		GetScriptApplicationInstance()->OnMouseWheel(mouse_x, mouse_y);
	}
	// 键按下
	void SdlApplication::OnKeyDown(unsigned int key) {
		GetScriptApplicationInstance()->OnKeyDown(key);
	}
	// 键松开
	void SdlApplication::OnKeyUp(unsigned int key) {
		GetScriptApplicationInstance()->OnKeyUp(key);
	}
	// 输入字符  
	void SdlApplication::OnChar(std::shared_ptr<VirtualCharEvent> e) {
		GetScriptApplicationInstance()->OnChar(e->GetCommitText().c_str());
	}

	// 加载 asset 资源
	bool SdlApplication::LoadAsset(std::string filepath, std::vector<int8_t>& buff) {
		SDL_RWops* file = SDL_RWFromFile(filepath.c_str(), "rb");
		if (!file) return false;
		Sint64 size = SDL_RWsize(file);
		buff.resize(size);
		SDL_RWread(file, buff.data(), size, 1);
		SDL_RWclose(file);
		return true;
	}

	// 获取脚本文本
	std::string SdlApplication::GetScriptTextByFilePath(std::string filepath) {
#ifdef AUTO_LOAD_SCRIPT_DATA
		static std::string script_directory = GetScriptDirectory().u8string();
		if (filepath.find(script_directory) == 0) {
			std::string res_path = filepath.substr(script_directory.size() + 1);
			std::string res_name = LuaGuiResource::ToResourceName(res_path);
			LuaGuiResource* res = LuaGuiResource::Get(res_name.c_str());
			if (res) return std::string(res->begin(), res->end());
		}
	
#endif // AUTO_LOAD_SCRIPT_DATA

		return GetAssetTextByFilePath(filepath);
	}

	std::string SdlApplication::GetTextByFilePath(std::string filepath) {
		return Application::GetTextByFilePath(filepath);
	}

	void SdlApplication::OnWindowSize() {
		Application::OnWindowSize();
		// 重新获取屏幕宽高
		if (m_screen_width < m_window_width || m_screen_height < m_window_height) {
			SDL_DisplayMode dm;
			if (SDL_GetDesktopDisplayMode(0, &dm) == 0) {
				this->SetScreenWidthHeight(dm.w, dm.h);
			}
		}

		GetScriptApplicationInstance()->OnSize(m_window_width, m_window_height);
	}

	void SdlApplication::OnScreenSize() {
		Application::OnScreenSize();
		Uint32 rmask, gmask, bmask, amask;
		// 0xrgba = > sdl 0xargb = > cairo 颜色格式
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
		amask = 0x000000ff;
		rmask = 0x0000ff00;
		gmask = 0x00ff0000;
		bmask = 0xff000000;
#else
		// cairo 0xargb
		amask = 0xff000000;
		rmask = 0x00ff0000;
		gmask = 0x0000ff00;
		bmask = 0x000000ff;
#endif
		if (m_cr) {
			cairo_surface_t* surface = cairo_get_target(m_cr);
			cairo_surface_destroy(surface);
			cairo_destroy(m_cr);
			m_cr = nullptr;
		}

		if (m_sdl_surface) {
			SDL_FreeSurface(m_sdl_surface);
			m_sdl_surface = nullptr;
		}

		m_sdl_surface = SDL_CreateRGBSurface(0, m_screen_width, m_screen_height, 32, rmask, gmask, bmask, amask);
		cairo_surface_t* surface = cairo_image_surface_create_for_data((unsigned char*)m_sdl_surface->pixels, CAIRO_FORMAT_ARGB32, m_screen_width, m_screen_height, m_sdl_surface->pitch);
		m_cr = cairo_create(surface);

		if (m_paint) m_paint->SetCairo(m_cr);
	}

	std::string SdlApplication::GetClipboardText() {
		if (SDL_HasClipboardText() == SDL_FALSE) return "";
		char* str = SDL_GetClipboardText();
		std::string text = str;
		SDL_free(str);
		return text;
	}

	bool SdlApplication::SetClipboardText(std::string text) {
		return 0 == SDL_SetClipboardText(text.c_str());
	}
}

#endif // USE_SDL
